Skip to content

[DO NOT MERGE] Implement operator commands for Standalone Activities - #3013

Open
GregoryTravis wants to merge 60 commits into
mainfrom
gmt/operator-commands
Open

[DO NOT MERGE] Implement operator commands for Standalone Activities#3013
GregoryTravis wants to merge 60 commits into
mainfrom
gmt/operator-commands

Conversation

@GregoryTravis

@GregoryTravis GregoryTravis commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Implement operator commands for standalone activities

Adds pause, unpause, reset, and update-options to standalone activities, plus
the describe surface needed to observe their effects.

Standalone activities already supported start, result, describe, cancel, and
terminate. This adds the four operator commands the server exposes for them, so
an operator can hold, resume, restart, and retune a running activity without
going through a workflow.

Describe: payload fields are opt-in

DescribeActivityExecutionRequest gates four payload-bearing fields behind
per-call flags (api#792). All four are now plumbed through
DescribeActivityOptions and default to false.

@GregoryTravis
GregoryTravis requested a review from a team as a code owner August 18, 2026 01:20

@maciejdudko maciejdudko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, although there are a few design choices that warrant more discussion.

Comment thread temporal-sdk/src/main/java/io/temporal/client/ActivityExecutionDescription.java Outdated
Comment thread temporal-sdk/src/main/java/io/temporal/client/ActivityExecutionDescription.java Outdated
Comment thread temporal-sdk/src/main/java/io/temporal/client/ActivityExecutionDescription.java Outdated
Comment on lines +423 to +424
private final @Nullable String reason;
private final @Nullable Duration jitter;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private final @Nullable String reason;
private final @Nullable Duration jitter;
private final UnpauseActivityOptions options;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maciejdudko I also added one for Pause, even though there is just one field, for future proofing.


UntypedActivityHandle handle = newHandle();

handle.pause("because");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol

The paused execution status was only asserted incidentally at the tail of
updateOptionsOnPausedActivity. Cover the transition on its own: the same
handle reports RUNNING before the pause and PAUSED after, on both the
execution status and the run state.

Matches the equivalent test in the Ruby, Python and Go suites.
An update naming nothing sent an empty field mask and silently changed
nothing. Throw IllegalArgumentException before the round trip instead,
pointing at restoreOriginalOptions for reverting.

Matches Ruby, which raises ArgumentError, and Go and Python, which reject
the same call.
unobservableRequestFields checked only reset_heartbeat. Set keep_paused
and restore_original_options too and assert all three, since none of
them is visible in any observable server state.
Ported from sdk-python#1782, written against our current API shape.

Replaces describePayloadFieldsAreOptIn, describeReadsInputAndOutcome and
describeReadsFailureOutcome with one test covering all three, and more:

- a new HeartbeatFailIncrementActivity heartbeats, fails once, then
  succeeds, so a single describe carries input, result, heartbeat details
  and a last failure at the same time. The three tests it replaces each
  used a different activity, so no describe ever held them together.
- pins hasLastFailure true while getOutcomeFailure is null on a succeeded
  activity that failed once, the terminal-versus-attempt distinction that
  was untested.
- asserts the accessors are absent, not merely that has* is false.

The activity takes and returns Integer rather than int: MethodExtractor
cannot probe a method reference with primitive types.
The four api#792 flags were covered only functionally, so nothing proved
the SDK actually sets them on DescribeActivityExecutionRequest — a
default-on bug would have looked identical from observable state.

Three cases against a stubbed client: defaults ask for nothing, all four
are forwarded, and asking for one does not set the others.

Brings Java level with Python and Go.
The stripping in RootActivityClientInvoker existed with no coverage. It
only matters against a server that ignores the opt-ins, which no
functional test can produce, so it needs a stub that returns every
payload field regardless of what was asked for.

Three cases: nothing requested strips all four, everything requested
keeps all four, and stripping is per field.
It asserted that a single set option produces a mask naming exactly that
option. The mask tests in every SDK already assert the mask names exactly
what changed, and a one-option case catches nothing the multi-option case
misses.

Java was the only SDK with it, so removing it is also parity rather than
porting three near-duplicates.
RestoreOriginalOptions reuses the update-options interceptor rather than
having one of its own, distinguished purely by the restore flag with an
empty mask. An interceptor watching option updates would otherwise
silently miss restores, and nothing pinned that.

Ported from the Python interceptor suite, which was the only one
asserting it.
Java clears the same way Go does, by passing a zero value rather than a
dedicated sentinel: Duration.ZERO is non-null so the path reaches the
mask, and the server normalizes a zero timeout back to unset. Null keeps
the path out of the mask entirely.

Neither behaviour was covered. Brings Java level with Go and Python.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants